package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserEnterpriseService;
import com.evil.activiti.api.RemoteActivityService;
import com.evil.application.api.RemoteApplicationProcessNodeService;
import com.evil.application.constant.ApplicationConstant;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.mapper.ApplicationOrderMapper;
import com.evil.application.mapper.ApplicationProcessMapper;
import com.evil.application.mapper.ApplicationProcessNodeMapper;
import com.evil.application.pojo.bo.application.process.node.ProcessNodeBO;
import com.evil.application.pojo.dto.form.control.ControlBaseInfoDTO;
import com.evil.application.pojo.dto.permission.control.ControlPermissionDTO;
import com.evil.application.pojo.dto.permission.control.ModifyControlPermissionReqDTO;
import com.evil.application.pojo.dto.process.node.save.CopyNodeDTO;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.pojo.entity.ApplicationOrder;
import com.evil.application.pojo.entity.ApplicationProcess;
import com.evil.application.pojo.entity.ApplicationProcessNode;
import com.evil.application.service.*;
import com.evil.application.util.ApplicationUtil;
import com.evil.application.util.OrderDataUtil;
import com.evil.common.account.dto.user.CheckEnterpriseUserIdsReqDTO;
import com.evil.common.account.dto.user.FindUserIdsByIdReqDTO;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.account.dto.user.UserEnterpriseDTO;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.activiti.enums.ApprovalTypeEnum;
import com.evil.common.activiti.enums.FallbackTypeEnum;
import com.evil.common.activiti.enums.NodeTypeEnum;
import com.evil.common.activiti.enums.PendingDivertTypeEnum;
import com.evil.common.application.dto.judge.CopyJudgeReqDTO;
import com.evil.common.application.dto.judge.JudgeOr;
import com.evil.common.application.dto.judge.JudgeSourceDTO;
import com.evil.common.application.dto.judge.JudgesDTO;
import com.evil.common.application.dto.order.node.OrderNodeReqDTO;
import com.evil.common.application.dto.permission.app.*;
import com.evil.common.application.dto.permission.control.ControlPermissionSourceDTO;
import com.evil.common.application.dto.permission.control.ControlPermissionsReqDTO;
import com.evil.common.application.dto.permission.control.CopyControlPermissionReqDTO;
import com.evil.common.application.dto.process.DynamicHandlerControlListReqDTO;
import com.evil.common.application.dto.process.ProcessTaskReqDTO;
import com.evil.common.application.dto.process.node.*;
import com.evil.common.application.dto.process.node.back.FallbackConfigDTO;
import com.evil.common.application.dto.process.node.btn.NodeButtonConfigDTO;
import com.evil.common.application.dto.process.node.gateway.GatewayConfigDTO;
import com.evil.common.application.dto.process.node.setting.NodeControlSettingDTO;
import com.evil.common.application.dto.process.node.submit.SubmitRefreshConfigDTO;
import com.evil.common.application.dto.robot.RobotConfigDTO;
import com.evil.common.application.dto.robot.rule.CopyRobotRuleReqDTO;
import com.evil.common.application.dto.robot.rule.RobotRuleSourceDTO;
import com.evil.common.application.dto.robot.rule.RobotRulesDTO;
import com.evil.common.application.enums.ApplicationSourceTypeEnum;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.PublishStatusEnum;
import com.evil.common.application.enums.form.ControlPermissionSourceTypeEnum;
import com.evil.common.application.enums.judge.JudgeSourceTypeEnum;
import com.evil.common.application.enums.permission.ApplicationPermissionTypeEnum;
import com.evil.common.application.enums.robot.RobotRuleSourceTypeEnum;
import com.evil.common.application.exception.ApplicationException;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SpecialFieldEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 应用流程节点表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationProcessNodeServiceImpl extends ServiceImpl<ApplicationProcessNodeMapper, ApplicationProcessNode>
        implements ApplicationProcessNodeService, RemoteApplicationProcessNodeService {

    private final LoginUtil loginUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationProcessMapper applicationProcessMapper;

    private final ApplicationOrderMapper applicationOrderMapper;

    private final ApplicationFormControlService applicationFormControlService;

    private final ApplicationJudgeService applicationJudgeService;

    private final ApplicationPermissionService applicationPermissionService;

    private final ControlPermissionService controlPermissionService;

    private final ApplicationRobotRuleService applicationRobotRuleService;

    private final RemoteUserEnterpriseService remoteUserEnterpriseService;

    private final RemoteActivityService remoteActivityService;

    private final ApplicationLogServiceImpl logService;

    /**
     * 应用发布版 节点列表
     *
     * @param applicationId applicationId
     * @return List
     */
    @Override
    public List<ApplicationProcessNode> appReleaseNodes(Long applicationId) {
        ApplicationProcess process = applicationProcessMapper.findByAppIdAndVersion(applicationId, ApplicationVersionEnum.RELEASE_VERSION.getId());
        return this.findByProcessId(process.getProcessId());
    }

    /**
     * 应用流程节点列表
     *
     * @param processId processId
     * @return List
     */
    @Override
    public List<ApplicationProcessNode> findByProcessId(Long processId) {
        return this.baseMapper.findByProcessId(processId, true);
    }

    /**
     * 获取节点基础信息
     *
     * @param id            id
     * @param filterDeleted filterDeleted
     * @return NodeBaseDTO
     */
    @Override
    public NodeBaseDTO findBaseById(Long id, boolean filterDeleted) {
        return this.baseMapper.findBaseById(id, filterDeleted);
    }

    /**
     * 获取节点基础信息列表
     *
     * @param ids           ids
     * @param filterDeleted filterDeleted
     * @return NodeBaseDTO 列表
     */
    @Override
    public List<NodeBaseDTO> findBaseByIds(Collection<Long> ids, boolean filterDeleted) {
        return this.baseMapper.findBaseByIds(ids, filterDeleted);
    }

    /**
     * 获取节点基础信息列表
     *
     * @param ids           ids
     * @param filterDeleted filterDeleted
     * @return NodeBaseDTO 列表
     */
    @Override
    public Map<Long, NodeBaseDTO> findBaseMapByIds(Collection<Long> ids, boolean filterDeleted) {
        return this.baseMapper.findBaseMapByIds(ids, filterDeleted);
    }

    /**
     * 获取流程节点信息列表
     *
     * @param process process
     * @return NodeRespInfoDTO 列表
     */
    @Override
    public List<NodeRespInfoDTO> findByProcess(ApplicationProcess process) {
        return this.toNodeRespInfoDTO(process, this.baseMapper.findByProcessId(process.getProcessId(), true));
    }

    /**
     * 获取应用开始节点
     *
     * @param applicationId applicationId
     * @return ApplicationProcessNode
     */
    @Override
    public ApplicationProcessNode findStartNode(Long applicationId) {
        ApplicationProcess process = applicationProcessMapper.findByAppIdAndVersion(applicationId, ApplicationVersionEnum.RELEASE_VERSION.getId());
        return this.baseMapper.findStartNodeByProcessId(process.getProcessId());
    }

    /**
     * 获取应用开始节点map
     *
     * @param appIds      appIds
     * @param versionEnum versionEnum
     * @return key:appId, value:nodeId
     */
    @Override
    public Map<Long, Long> findAppStartNodeMap(Collection<Long> appIds, ApplicationVersionEnum versionEnum) {
        // 应用流程列表
        List<ApplicationProcess> processes = applicationProcessMapper.findByAppIdsAndVersion(appIds, versionEnum.getId());
        Map<Long, Long> appProcessMap = StreamUtil.toMapKV(processes, ApplicationProcess::getApplicationId, ApplicationProcess::getProcessId);

        // 流程开始节点列表
        List<ApplicationProcessNode> startNodes = this.baseMapper.findStartNodeByProcessIds(appProcessMap.values());
        Map<Long, Long> processStartNodeMap = StreamUtil.toMapKV(startNodes, ApplicationProcessNode::getProcessId, ApplicationProcessNode::getNodeId);

        return StreamUtil.transMapV(appProcessMap, processStartNodeMap::get);
    }

    /**
     * 复制应用流程节点
     *
     * @param source       source
     * @param target       target
     * @param controlIdMap 新旧控件id映射
     * @param updateSource 是否需要更新源信息
     * @return Map 新旧节点id映射
     */
    @Override
    public Map<Long, Long> copyNode(ApplicationProcess source, ApplicationProcess target, Map<Long, Long> controlIdMap, boolean updateSource) {
        // 源节点列表
        List<ApplicationProcessNode> sourceNodes = this.baseMapper.findByProcessId(source.getProcessId(), true);
        // 源节点map
        Map<Long, ApplicationProcessNode> sourceNodeMap = StreamUtil.toMapK(sourceNodes, ApplicationProcessNode::getNodeId);
        // 源节点树
        List<ProcessNodeBO> sourceNodeTree = ApplicationUtil.classifyProcessNode(sourceNodes);

        // 目标节点列表
        List<ApplicationProcessNode> targetNodes = this.baseMapper.findByProcessId(target.getProcessId(), true);
        // 目标节点与源映射map
        Map<Long, ApplicationProcessNode> targetSourceNodeMap = StreamUtil.toMapFK(targetNodes,
                e -> !ApplicationUtil.DEFAULT_SOURCE_NODE_ID.equals(e.getSourceNodeId()), ApplicationProcessNode::getSourceNodeId);

        // 递归复制节点
        CopyNodeDTO copyNodeDTO = new CopyNodeDTO(target, controlIdMap);
        this.recursionCopyNode(targetSourceNodeMap, null, sourceNodeTree, copyNodeDTO, updateSource);

        // 是否有校验错误
        if (copyNodeDTO.hasError()) {
            throw new ApplicationException(copyNodeDTO.getErrorMsgList());
        }

        // 删除失效节点
        this.deleteNodes(StreamUtil.filter(targetNodes, e -> !sourceNodeMap.containsKey(e.getSourceNodeId())));

        // 新旧节点id映射
        return copyNodeDTO.getNodeIdMap();
    }

    /**
     * 初始化流程节点
     *
     * @param process process
     */
    @Override
    public void initNode(ApplicationProcess process) {
        ApplicationVersionEnum versionEnum = ApplicationVersionEnum.findById(process.getApplicationVersion());
        Set<Long> controlIds = applicationFormControlService.findBaseInfoMapByAppId(process.getApplicationId(), versionEnum, true).keySet();
        List<ControlPermissionDTO> controlPermissions = StreamUtil.transListT(controlIds, ControlPermissionDTO::new);

        ModifyControlPermissionReqDTO modifyControlPermissionReqDTO = new ModifyControlPermissionReqDTO();
        modifyControlPermissionReqDTO.setEnterpriseId(process.getEnterpriseId());
        modifyControlPermissionReqDTO.setControlPermissions(controlPermissions);
        modifyControlPermissionReqDTO.setControlIds(controlIds);

        // 开始节点
        ApplicationProcessNode beginNode = this.createProcessNode(process);
        beginNode.setNodeType(NodeTypeEnum.START_EVENT.getId());
        beginNode.setNodeName(NodeTypeEnum.START_EVENT.getDefaultNodeName());
        beginNode.setParentNodeId(ApplicationUtil.DEFAULT_PARENT_NODE_ID);
        logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, beginNode);
        // 开始节点控件权限
        modifyControlPermissionReqDTO.setControlPermissionSourceDTO(
                new ControlPermissionSourceDTO(ControlPermissionSourceTypeEnum.PROCESS_NODE, beginNode.getNodeId()));
        controlPermissionService.saveControlPermissions(modifyControlPermissionReqDTO);

        // 结束节点
        ApplicationProcessNode endNode = this.createProcessNode(process);
        endNode.setNodeType(NodeTypeEnum.END_EVENT.getId());
        endNode.setNodeName(NodeTypeEnum.END_EVENT.getDefaultNodeName());
        endNode.setParentNodeId(beginNode.getNodeId());
        logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, endNode);
        // 结束节点控件权限
        modifyControlPermissionReqDTO.setControlPermissionSourceDTO(
                new ControlPermissionSourceDTO(ControlPermissionSourceTypeEnum.PROCESS_NODE, beginNode.getNodeId()));
        controlPermissionService.saveControlPermissions(modifyControlPermissionReqDTO);
    }

    /**
     * 删除流程节点
     *
     * @param process process
     */
    @Override
    public void deleteProcessNode(ApplicationProcess process) {
        this.deleteNodes(this.baseMapper.findByProcessId(process.getProcessId(), true));
    }

    /**
     * 添加应用流程节点
     *
     * @param addNodeReqDTO addNodeReqDTO
     * @return NodeDTO
     */
    @Override
    public NodeRespInfoDTO addApplicationProcessNode(AddNodeReqDTO addNodeReqDTO) {
        Long preNodeId = addNodeReqDTO.getPreNodeId();
        Integer nodeType = addNodeReqDTO.getNodeType();
        boolean afterBranch = SwitchEnum.isOpen(addNodeReqDTO.getAfterBranch());
        boolean isNewBranch = SwitchEnum.isOpen(addNodeReqDTO.getIsNewBranch());
        NodeTypeEnum nodeTypeEnum = NodeTypeEnum.isExistById(nodeType);
        if (!nodeTypeEnum.isCanAdd()) {
            throw NodeTypeEnum.getBusinessException();
        }

        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationProcessNode preProcessNode = this.baseMapper.findById(preNodeId, true);

        ApplicationProcess applicationProcess = applicationProcessMapper.findById(preProcessNode.getProcessId());

        // 不允许编辑发布版
        if (ApplicationVersionEnum.RELEASE_VERSION.getId() == applicationProcess.getApplicationVersion()) {
            throw new BusinessException(RCodeEnum.ACCESS_DENIED);
        }

        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationProcess.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        List<ApplicationProcessNode> processNodeList = this.baseMapper.findByParentNodeId(preNodeId, true);

        ApplicationProcessNode applicationProcessNode = this.createProcessNode(applicationProcess);
        applicationProcessNode.setParentNodeId(preProcessNode.getNodeId());
        applicationProcessNode.setNodeType(nodeType);
        // 如果是填写节点，不允许回退
        if (nodeType == NodeTypeEnum.FILL_IN_NODE.getId()) {
            applicationProcessNode.setNodeFallback(SwitchEnum.NO.getId());
        }

        applicationProcessNode.setNodeName(addNodeReqDTO.getNodeName());
        logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, applicationProcessNode);

        // 非网关节点 处理父级原子级列表
        if (NodeTypeEnum.INCLUSIVE_GATEWAY_NODE.getId() != addNodeReqDTO.getNodeType()) {
            List<ApplicationProcessNode> childList = processNodeList.stream()
                    // 如果在分支之后创建节点，则不需要把分支节点过继到新节点
                    .filter(e -> !afterBranch || NodeTypeEnum.INCLUSIVE_GATEWAY_NODE.getId() != e.getNodeType())
                    .peek(e -> e.setParentNodeId(applicationProcessNode.getNodeId()))
                    .collect(Collectors.toList());

            logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, childList);
        }
        // 网关节点
        else {
            // 如果不是新分支体系 则需要把原有子级挂在到新的
            if (!isNewBranch) {
                // TODO
            }
        }

        if (nodeTypeEnum.isRobot()) {
            // 机器人默认 处理人 发发起人
            ModifyAppPermissionReqDTO pendingDiverts = new ModifyAppPermissionReqDTO();
            pendingDiverts.setEnterpriseId(applicationProcess.getEnterpriseId());
            pendingDiverts.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER.getId());
            pendingDiverts.setSourceId(applicationProcessNode.getNodeId());

            AppPermissionDTO appPermissionDTO = new AppPermissionDTO();
            appPermissionDTO.setPermissionType(ApplicationPermissionTypeEnum.DYNAMIC_APPLICANT.getId());
            appPermissionDTO.setTargetId(ApplicationUtil.DEFAULT_PERMISSION_TARGET_ID);
            appPermissionDTO.setSourceId(applicationProcessNode.getNodeId());
            appPermissionDTO.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER.getId());
            pendingDiverts.setPermissions(ListUtil.toList(appPermissionDTO));
            applicationPermissionService.modifyPermission(pendingDiverts);
        }

        // 有未部署
        applicationInfo.setPublishStatus(PublishStatusEnum.UN_PUBLISH.getId());
        logService.saveAndCheck(applicationInfoMapper, ApplicationInfo::getApplicationId, applicationInfo);

        // 回显
        processNodeList = this.baseMapper.findByParentNodeId(preNodeId, true);
        processNodeList.add(preProcessNode);
        List<NodeRespInfoDTO> nodeRespInfoList = this.toNodeRespInfoDTO(applicationProcess, processNodeList);
        // 从上上级获取当前上级
        return ApplicationUtil.classifyNode(nodeRespInfoList, preProcessNode.getParentNodeId());
    }

    /**
     * 删除应用流程节点
     *
     * @param nodeId nodeId
     */
    @Override
    public void deleteApplicationProcessNode(Long nodeId) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 获取 被删除节点
        ApplicationProcessNode applicationProcessNode = this.baseMapper.findById(nodeId, true);

        // 获取流程
        ApplicationProcess applicationProcess = applicationProcessMapper.findById(applicationProcessNode.getProcessId());

        // 不允许编辑发布版
        if (ApplicationVersionEnum.RELEASE_VERSION.getId() == applicationProcess.getApplicationVersion()) {
            throw new BusinessException(RCodeEnum.ACCESS_DENIED);
        }

        // 获取应用
        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationProcess.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        // 判断该节点是否能删除  根节点不能删除
        if (ApplicationUtil.DEFAULT_PARENT_NODE_ID.equals(applicationProcessNode.getParentNodeId())) {
            throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_NODE_CANT_DELETE);
        }

        // 全部需要操作节点列表
        List<ApplicationProcessNode> modifyNodes = new ArrayList<>();

        // 需要删除的节点列表
        List<ApplicationProcessNode> deleteNodes = new ArrayList<>();
        deleteNodes.add(applicationProcessNode);

        if (NodeTypeEnum.INCLUSIVE_GATEWAY_NODE.getId() == applicationProcessNode.getNodeType()) {
            // 网关节点
            List<ApplicationProcessNode> children = this.baseMapper.findByParentNodeId(applicationProcessNode.getParentNodeId(), true);
            List<ApplicationProcessNode> gateway = StreamUtil.filter(children, e -> NodeTypeEnum.INCLUSIVE_GATEWAY_NODE.getId() == e.getNodeType());
            if (gateway.size() == 2) {
                // 如果只剩两个网关节点了，需要一并删除
                deleteNodes = gateway;
            }

            List<Long> deleteNodeIds = deleteNodes.stream().map(ApplicationProcessNode::getNodeId).collect(Collectors.toList());
            // 一并删除 被删除节点的所有子节点
            deleteNodes.addAll(this.baseMapper.findByParentNodeIds(deleteNodeIds, true));
        } else {
            // 获取 被删除节点的 所有子节点
            List<ApplicationProcessNode> deleteNodeChildren = this.baseMapper.findByParentNodeId(nodeId, true);
            // 把 被删除节点 的所有子节点 放在 被删除节点的父节点下
            deleteNodeChildren.forEach(e -> e.setParentNodeId(applicationProcessNode.getParentNodeId()));
            modifyNodes.addAll(deleteNodeChildren);
        }
        deleteNodes.forEach(e -> e.setIsDeleted(SwitchEnum.YES.getId()));

        // 把删除节点列表 push 到全部需要操作节点列表
        modifyNodes.addAll(deleteNodes);

        logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, modifyNodes);

        // 变更节点控件配置
        List<Long> nodeIds = modifyNodes.stream().map(ApplicationProcessNode::getNodeId).collect(Collectors.toList());
        controlPermissionService.deleteControlPermissions(new ControlPermissionsReqDTO(ControlPermissionSourceTypeEnum.PROCESS_NODE, nodeIds));

        // 有未部署
        applicationInfo.setPublishStatus(PublishStatusEnum.UN_PUBLISH.getId());
        logService.saveAndCheck(applicationInfoMapper, ApplicationInfo::getApplicationId, applicationInfo);
    }

    /**
     * 编辑应用流程节点
     *
     * @param modifyNodeReqDTO modifyNodeReqDTO
     */
    @Override
    public void modifyApplicationProcessNode(ModifyNodeInfoReqDTO modifyNodeReqDTO) {
        Long nodeId = modifyNodeReqDTO.getNodeId();
        Integer nodeType = modifyNodeReqDTO.getNodeType();
        List<AppPermissionDTO> handlers = modifyNodeReqDTO.getHandlers();
        List<NodeControlSettingDTO> nodeControlSettings = modifyNodeReqDTO.getNodeControlSettings();

        NodeTypeEnum nodeTypeEnum = NodeTypeEnum.isExistById(nodeType);
        if (NodeTypeEnum.END_EVENT == nodeTypeEnum) {
            throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_END_NODE_CANT_EDIT);
        }
        if (nodeTypeEnum.isHasHandler()) {
            // 有处理人
            if (handlers.isEmpty()) {
                throw new BusinessException(RCodeEnum.APPLICATION_PROCESS_NODE_HANDLERS_EMPTY);
            }
        }

        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());
        // 检查参数
        NodeTypeEnum.isExistById(nodeType);

        if (NodeTypeEnum.APPROVAL_NODE == nodeTypeEnum) {
            // 审批类型
            ApprovalTypeEnum.isExistById(modifyNodeReqDTO.getApprovalType());
        }

        // 获取节点信息
        ApplicationProcessNode applicationProcessNode = this.baseMapper.findById(nodeId, true);

        // 获取应用流程信息
        ApplicationProcess process = applicationProcessMapper.findById(applicationProcessNode.getProcessId());
        Long applicationId = process.getApplicationId();
        ApplicationVersionEnum versionEnum = ApplicationVersionEnum.findById(process.getApplicationVersion());

        // 不允许编辑发布版
        if (ApplicationVersionEnum.RELEASE_VERSION == versionEnum) {
            throw new BusinessException(RCodeEnum.ACCESS_DENIED);
        }

        // 获取应用信息
        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationId);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        // 获取发布版 控件code map
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(applicationId, versionEnum, true);

        // 赋值
        BeanUtil.copyProperties(modifyNodeReqDTO, applicationProcessNode, NodeInfoDTO.IGNORE_PROPERTIES);

        // 是否开启重新触发数据关联或公式
        if (modifyNodeReqDTO.getRecalculation() == SwitchEnum.YES.getId()) {
            applicationProcessNode.setRecalculationControlIds(JSON.toJSONString(modifyNodeReqDTO.getRecalculationControlIds()));
        }

        // 网关配置(node_type为0时值有效)
        if (NodeTypeEnum.INCLUSIVE_GATEWAY_NODE.getId() == nodeType) {
            // 检验与补全 应用流程节点网关校验 信息
            GatewayConfigDTO gatewayConfig = modifyNodeReqDTO.getGatewayConfig();
            // 检查 条件判断
            applicationJudgeService.checkJudge(gatewayConfig.getJudgeOr(), controlCodeMap, null);
            applicationProcessNode.setGatewayConfig(JSON.toJSONString(gatewayConfig));
        }

        // 机器人节点
        if (nodeTypeEnum.isRobot()) {
            RobotConfigDTO robotConfigDTO = modifyNodeReqDTO.getRobotConfig();
            if (null == robotConfigDTO.getTargetAppId()) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "目标应用不能为空");
            }
            // 检查是否成环
            this.checkRobotNodeSurround(applicationInfo.getApplicationId(), robotConfigDTO.getTargetAppId());
            // 获取目标应用信息
            ApplicationInfo targetApp = applicationInfoMapper.findById(robotConfigDTO.getTargetAppId());
            Long targetAppId = targetApp.getApplicationId();
            // 获取目标应用表单发布版控件信息
            Map<Long, String> targetControlCodeMap = applicationFormControlService.controlCodeMap(targetAppId, versionEnum, true);

            // 检查 条件判断
            applicationJudgeService.checkJudge(robotConfigDTO.getJudgeOrs(), targetControlCodeMap, null);
            // 保存条件
            applicationJudgeService.saveJudges(new JudgesDTO(JudgeSourceTypeEnum.PROCESS_ROBOT_NODE_SEARCH, process.getEnterpriseId(), nodeId, robotConfigDTO.getJudgeOrs()));

            // 保存机器人规则
            RobotRulesDTO robotRulesDTO = new RobotRulesDTO();
            robotRulesDTO.setEnterpriseId(process.getEnterpriseId());
            robotRulesDTO.setSourceType(RobotRuleSourceTypeEnum.PROCESS_NODE_ROBOT.getId());
            robotRulesDTO.setSourceId(nodeId);
            robotRulesDTO.setRobotRules(robotConfigDTO.getRobotRules());
            robotRulesDTO.setTargetApplicationId(robotConfigDTO.getTargetAppId());
            applicationRobotRuleService.saveRobotRule(robotRulesDTO);

            robotConfigDTO.setRobotRules(new ArrayList<>());
            robotConfigDTO.setJudgeOrs(new ArrayList<>());
            applicationProcessNode.setRobotConfig(JSON.toJSONString(robotConfigDTO));
        }

        // 节点提交重新更新字段关联数据配置
        if (SwitchEnum.isOpen(modifyNodeReqDTO.getSubmitRefresh())) {
            List<SubmitRefreshConfigDTO> submitRefreshConfig = modifyNodeReqDTO.getSubmitRefreshConfig();
            if (null != submitRefreshConfig) {
                Set<Long> submitRefreshControlIds = submitRefreshConfig.stream().map(SubmitRefreshConfigDTO::getControlId).collect(Collectors.toSet());
                if (!controlCodeMap.keySet().containsAll(submitRefreshControlIds)) {
                    throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "开启节点提交重新更新字段，表单控件不存在");
                }
                applicationProcessNode.setSubmitRefreshConfig(JSON.toJSONString(submitRefreshConfig));
            }
        }

        // 流程回退配置
        if (SwitchEnum.isOpen(modifyNodeReqDTO.getNodeFallback())) {
            FallbackConfigDTO nodeFallbackConfig = modifyNodeReqDTO.getNodeFallbackConfig();
            if (null != nodeFallbackConfig) {
                Set<Long> nodeIds = new HashSet<>(nodeFallbackConfig.getFallbackNodeIds());
                if (nodeIds.isEmpty() && FallbackTypeEnum.ASSIGN_BEFORE.getId() == nodeFallbackConfig.getFallbackType()) {
                    throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "流程回退配置，回退类型为指定节点时，指定节点列表不能为空");
                }
                List<ApplicationProcessNode> nodeList = this.baseMapper.findByIds(nodeIds, false);
                if (nodeList.size() != nodeIds.size()) {
                    throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "流程回退配置，节点不存在");
                }
                applicationProcessNode.setNodeFallbackConfig(JSON.toJSONString(nodeFallbackConfig));
            }
        }

        // 流程按钮配置
        applicationProcessNode.setNodeButtonConfig(JSON.toJSONString(modifyNodeReqDTO.getNodeButtonConfig()));

        logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, applicationProcessNode);

        // 节点超时提醒人列表
        if (SwitchEnum.isOpen(applicationProcessNode.getOvertimeRemind())) {
            ModifyAppPermissionReqDTO overtimeNotifiers = new ModifyAppPermissionReqDTO();
            overtimeNotifiers.setEnterpriseId(process.getEnterpriseId());
            overtimeNotifiers.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_OVERTIME_NOTIFIER.getId());
            overtimeNotifiers.setSourceId(applicationProcessNode.getNodeId());
            overtimeNotifiers.setPermissions(modifyNodeReqDTO.getOvertimeReminds());
            applicationPermissionService.modifyPermission(overtimeNotifiers);
        }

        // 待办转交人列表
        if (SwitchEnum.isOpen(applicationProcessNode.getPendingDivert())) {
            ModifyAppPermissionReqDTO pendingDiverts = new ModifyAppPermissionReqDTO();
            pendingDiverts.setEnterpriseId(process.getEnterpriseId());
            pendingDiverts.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_PENDING_DIVERT.getId());
            pendingDiverts.setSourceId(applicationProcessNode.getNodeId());
            pendingDiverts.setPermissions(modifyNodeReqDTO.getPendingDiverts());
            applicationPermissionService.modifyPermission(pendingDiverts);
        }

        // 流程节点处理人
        ModifyAppPermissionReqDTO pendingDiverts = new ModifyAppPermissionReqDTO();
        pendingDiverts.setEnterpriseId(process.getEnterpriseId());
        pendingDiverts.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER.getId());
        pendingDiverts.setSourceId(applicationProcessNode.getNodeId());
        pendingDiverts.setPermissions(handlers);
        applicationPermissionService.modifyPermission(pendingDiverts);

        // 节点控件配置列表
        if (nodeTypeEnum.hasControlPermission()) {
            ModifyControlPermissionReqDTO modifyControlPermissionReqDTO = new ModifyControlPermissionReqDTO();
            modifyControlPermissionReqDTO.setEnterpriseId(process.getEnterpriseId());
            modifyControlPermissionReqDTO.setControlPermissionSourceDTO(new ControlPermissionSourceDTO(ControlPermissionSourceTypeEnum.PROCESS_NODE, nodeId));
            modifyControlPermissionReqDTO.setControlPermissions(StreamUtil.transListT(nodeControlSettings, ControlPermissionDTO::new));
            modifyControlPermissionReqDTO.setControlIds(controlCodeMap.keySet());
            controlPermissionService.saveControlPermissions(modifyControlPermissionReqDTO);
        }

        // 有未部署
        applicationInfo.setPublishStatus(PublishStatusEnum.UN_PUBLISH.getId());
        logService.saveAndCheck(applicationInfoMapper, ApplicationInfo::getApplicationId, applicationInfo);
    }

    /**
     * 获取流程节点 可配置回退节点列表
     *
     * @param nodeId nodeId
     * @return List
     */
    @Override
    public List<NodeBaseDTO> canSettingFallbacks(Long nodeId) {
        ApplicationProcessNode processNode = this.baseMapper.findById(nodeId, true);

        // 流程信息
        ApplicationProcess applicationProcess = applicationProcessMapper.findById(processNode.getProcessId());

        // 应用信息
        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationProcess.getApplicationId());
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        return this.getCanSettingFallbacks(nodeId);
    }

    /**
     * 获取流程节点 可回退节点列表
     *
     * @param orderNodeReqDTO orderNodeReqDTO
     * @return List
     */
    @Override
    public List<NodeBaseDTO> canFallbacks(OrderNodeReqDTO orderNodeReqDTO) {
        UserEnterpriseDTO loginUserEnterprise = loginUtil.getLoginUserEnterprise();
        Long orderId = orderNodeReqDTO.getOrderId();
        Long nodeId = orderNodeReqDTO.getNodeId();

        ApplicationOrder applicationOrder = applicationOrderMapper.findById(orderId);
        ApplicationProcessNode processNode = this.baseMapper.findById(nodeId, true);

        // 流程信息
        applicationProcessMapper.isExistById(processNode.getProcessId());

        if (null == processNode.getNodeFallbackConfig()) {
            return new ArrayList<>();
        }

        List<Long> nodeIds = new ArrayList<>();
        FallbackConfigDTO fallbackConfigDTO = JSON.parseObject(processNode.getNodeFallbackConfig(), FallbackConfigDTO.class);
        // 之前所有节点
        if (FallbackTypeEnum.ALL_BEFORE.getId() == fallbackConfigDTO.getFallbackType()) {
            ProcessTaskReqDTO processTaskReqDTO = new ProcessTaskReqDTO();
            processTaskReqDTO.setProcessInstanceId(applicationOrder.getProcessInstanceId());
            processTaskReqDTO.setNode(BeanUtil.copyProperties(processNode, NodeBaseDTO.class));
            processTaskReqDTO.setAssignee(loginUserEnterprise);
            nodeIds = remoteActivityService.getCanFallbacks(processTaskReqDTO);
        }
        // 上一节点
        else if (FallbackTypeEnum.ONE_BEFORE.getId() == fallbackConfigDTO.getFallbackType()) {
            ProcessTaskReqDTO processTaskReqDTO = new ProcessTaskReqDTO();
            processTaskReqDTO.setProcessInstanceId(applicationOrder.getProcessInstanceId());
            processTaskReqDTO.setNode(BeanUtil.copyProperties(processNode, NodeBaseDTO.class));
            processTaskReqDTO.setAssignee(loginUserEnterprise);
            remoteActivityService.getCanFallbacks(processTaskReqDTO).stream().findFirst().ifPresent(nodeIds::add);
        }
        // 指定节点
        else if (FallbackTypeEnum.ASSIGN_BEFORE.getId() == fallbackConfigDTO.getFallbackType()) {
            // 指定节点
            nodeIds.addAll(fallbackConfigDTO.getFallbackNodeIds());
        }
        return this.baseMapper.findBaseByIds(nodeIds, true);
    }

    /**
     * 获取流程节点 可转交人员列表
     *
     * @param nodeId nodeId
     * @return List
     */
    @Override
    public List<UserBaseRespDTO> canReassignment(Long nodeId) {
        ApplicationProcessNode processNode = this.baseMapper.findById(nodeId, true);

        ApplicationProcess applicationProcess = applicationProcessMapper.findById(processNode.getProcessId());

        ApplicationInfo applicationInfo = applicationInfoMapper.findById(applicationProcess.getApplicationId());

        List<UserBaseRespDTO> pendingDiverts = new ArrayList<>();
        if (SwitchEnum.isOpen(processNode.getPendingDivert())) {
            if (PendingDivertTypeEnum.ALL.getId() == processNode.getPendingDivertType()) {
                // TODO 待优化，后面改成前端分页查询，这里不返回
                List<UserBaseRespDTO> userBases = remoteUserEnterpriseService.enterpriseUserBases(FindUserIdsByIdReqDTO.filterUnManager(applicationInfo.getEnterpriseId()));
                pendingDiverts.addAll(userBases);
            } else {
                AppPermissionSourceDTO appPermissionReqDTO = new AppPermissionSourceDTO();
                appPermissionReqDTO.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_PENDING_DIVERT.getId());
                appPermissionReqDTO.setSourceId(nodeId);
                pendingDiverts.addAll(applicationPermissionService.getPermissionUsersBySource(appPermissionReqDTO, true));
            }
        }
        return pendingDiverts;
    }

    /**
     * 获取兄弟整个家族节点
     *
     * @param brotherFamilyFlowReqDTO brotherFlowReqDTO
     * @return List
     */
    @Override
    public List<NodeRespInfoDTO> getBrotherFlowIds(BrotherFamilyFlowReqDTO brotherFamilyFlowReqDTO) {
        Long nodeId = brotherFamilyFlowReqDTO.getNodeId();
        List<Long> brotherNodeIds = brotherFamilyFlowReqDTO.getFilterBrotherNodeIds();

        // 当前节点
        ApplicationProcessNode node = this.baseMapper.findById(nodeId, true);
        ApplicationProcess applicationProcess = applicationProcessMapper.findById(node.getProcessId());

        // 第一个网关父级
        ApplicationProcessNode gatewayParentNode = this.getFirstGatewayParentNode(node, new ArrayList<>());

        List<NodeRespInfoDTO> nodes = new ArrayList<>();
        // 获取该网关所有子级
        if (null != gatewayParentNode) {
            List<ApplicationProcessNode> childrenNodes = this.baseMapper.findByParentNodeId(gatewayParentNode.getNodeId(), true);
            childrenNodes = this.recursionGetChildrenNodes(childrenNodes, new ArrayList<>());

            // 过滤 指定
            childrenNodes = childrenNodes.stream()
                    .filter(e -> null == brotherNodeIds || brotherNodeIds.contains(e.getNodeId()))
                    .collect(Collectors.toList());
            nodes = this.toNodeRespInfoDTO(applicationProcess, childrenNodes);
        }
        return nodes;
    }

    /**
     * 获取所有子级节点
     *
     * @param nodeId nodeId
     * @return List
     */
    @Override
    public List<NodeRespInfoDTO> getAllChildrenNodes(Long nodeId) {
        // 当前节点
        ApplicationProcessNode node = this.baseMapper.findById(nodeId, true);
        ApplicationProcess applicationProcess = applicationProcessMapper.findById(node.getProcessId());

        List<ApplicationProcessNode> childrenNodes = this.baseMapper.findByParentNodeId(node.getNodeId(), true);
        childrenNodes = this.recursionGetChildrenNodes(childrenNodes, new ArrayList<>());
        return this.toNodeRespInfoDTO(applicationProcess, childrenNodes);
    }

    /**
     * 获取动态处理人对应权限控件列表
     *
     * @param dynamicHandlerControlListReqDTO dynamicHandlerControlListReqDTO
     * @return List
     */
    @Override
    public List<ControlBaseInfoDTO> getDynamicHandlerControls(DynamicHandlerControlListReqDTO dynamicHandlerControlListReqDTO) {
        return null;
    }

    /**
     * 节点信息
     *
     * @param nodeId nodeId
     * @return NodeRespInfoDTO
     */
    @Override
    public NodeRespInfoDTO getNodeInfo(Long nodeId) {
        ApplicationProcessNode node = this.baseMapper.findById(nodeId, true);
        ApplicationProcess process = applicationProcessMapper.findById(node.getProcessId());
        ApplicationVersionEnum applicationVersionEnum = ApplicationVersionEnum.findById(process.getApplicationVersion());
        Set<Long> controlIds = applicationFormControlService.controlCodeMap(process.getApplicationId(), applicationVersionEnum, true).keySet();

        return this.createNodeRespDTO(node, controlIds);
    }

    /**
     * 节点应用key
     *
     * @param nodeId nodeId
     * @return NodeRespInfoDTO
     */
    @Override
    public String getApplicationKeyByNodeId(Long nodeId) {
        ApplicationProcessNode node = this.baseMapper.findById(nodeId, true);
        ApplicationProcess process = applicationProcessMapper.findById(node.getProcessId());
        ApplicationInfo applicationInfo = applicationInfoMapper.findById(process.getApplicationId());
        return applicationInfo.getApplicationKey();
    }

    /**
     * 获取节点处理人
     *
     * @param orderNodeReqDTO orderNodeReqDTO
     * @return List
     */
    @Override
    public List<UserEnterpriseDTO> getNodeHandlers(OrderNodeReqDTO orderNodeReqDTO) {
        Long nodeId = orderNodeReqDTO.getNodeId();
        Long orderId = orderNodeReqDTO.getOrderId();

        ApplicationProcessNode applicationProcessNode = this.baseMapper.findById(nodeId, false);

        ApplicationOrder applicationOrder = applicationOrderMapper.findById(orderId);
        Long enterpriseId = applicationOrder.getEnterpriseId();

        // 获取节点处理人
        AppPermissionUserReqDTO appPermissionReqDTO = new AppPermissionUserReqDTO();
        appPermissionReqDTO.setSourceId(applicationProcessNode.getNodeId());
        appPermissionReqDTO.setSourceType(ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER.getId());
        appPermissionReqDTO.setDynamic(applicationOrder);
        List<Long> nodeHandlers = applicationPermissionService.getPermissionUserIdsBySource(appPermissionReqDTO, true);

        // 过滤为当前企业正常用户
        nodeHandlers = remoteUserEnterpriseService.checkEnterpriseUserIds(CheckEnterpriseUserIdsReqDTO.filter(enterpriseId, nodeHandlers));

        return StreamUtil.transListT(nodeHandlers, e -> new UserEnterpriseDTO(enterpriseId, e));
    }

    /**
     * 递归复制流程节点
     *
     * @param targetSourceNodeMap targetSourceNodeMap
     * @param preNodeId           preNodeId
     * @param sourceNodeTree      sourceNodeTree
     * @param updateSource        是否更新源映射
     */
    private void recursionCopyNode(Map<Long, ApplicationProcessNode> targetSourceNodeMap,
                                   Long preNodeId,
                                   List<ProcessNodeBO> sourceNodeTree,
                                   CopyNodeDTO copyNodeDTO,
                                   boolean updateSource) {
        Map<Long, Long> controlIdMap = copyNodeDTO.getControlIdMap();
        Map<Long, Long> nodeIdMap = copyNodeDTO.getNodeIdMap();

        if (CollectionUtil.isEmpty(sourceNodeTree)) {
            return;
        }
        sourceNodeTree.forEach(node -> {
            ApplicationProcessNode copyNode;
            Long nodeId;
            if (targetSourceNodeMap.containsKey(node.getNodeId())) {
                // 编辑
                copyNode = targetSourceNodeMap.get(node.getNodeId());
                nodeId = copyNode.getNodeId();
            } else {
                // 复制
                nodeId = null;
                copyNode = new ApplicationProcessNode();
            }
            BeanUtil.copyProperties(node, copyNode, SpecialFieldEnum.getSpecialFields());
            // 获取重新触发数据关联或公式的控件id集合
            List<Long> controlIds = JSON.parseArray(node.getRecalculationControlIds(), Long.class);
            if (CollectionUtil.isNotEmpty(controlIds)) {
                // 替换 新旧映射控件ID
                controlIds = StreamUtil.transListFT(controlIds, controlIdMap::containsKey, controlIdMap::get);
                copyNode.setRecalculationControlIds(JSON.toJSONString(controlIds));
            }

            copyNode.setNodeId(nodeId);
            copyNode.setProcessId(copyNodeDTO.getProcess().getProcessId());
            copyNode.setSourceNodeId(node.getNodeId());
            copyNode.setParentNodeId(null == preNodeId ? ApplicationConstant.DEFAULT_PARENT_NODE_ID : preNodeId);

            NodeTypeEnum nodeTypeEnum = NodeTypeEnum.isExistById(node.getNodeType());

            // 处理回退配置
            if (nodeTypeEnum.canFallback()) {
                if (SwitchEnum.isOpen(copyNode.getNodeFallback()) && null != copyNode.getNodeFallbackConfig()) {
                    FallbackConfigDTO fallbackConfigDTO = JSON.parseObject(copyNode.getNodeFallbackConfig(), FallbackConfigDTO.class);
                    if (FallbackTypeEnum.ASSIGN_BEFORE.getId() == fallbackConfigDTO.getFallbackType()) {
                        // 这里新旧节点ID映射切换
                        List<Long> fallbackNodeIds = StreamUtil.transListFT(fallbackConfigDTO.getFallbackNodeIds(), nodeIdMap::containsKey, nodeIdMap::get);
                        fallbackConfigDTO.setFallbackNodeIds(fallbackNodeIds);
                        copyNode.setNodeFallbackConfig(JSON.toJSONString(fallbackConfigDTO));
                    }
                }
            }
            // 网关节点
            if (nodeTypeEnum.isGateway()) {
                if (null != copyNode.getGatewayConfig()) {
                    // 处理网关配置
                    GatewayConfigDTO gatewayConfigDTO = JSON.parseObject(node.getGatewayConfig(), GatewayConfigDTO.class);
                    // 检查 条件
                    applicationJudgeService.checkJudge(gatewayConfigDTO.getJudgeOr(), null, controlIdMap);
                    // 复制 条件
                    applicationJudgeService.copyJudges(new CopyJudgeReqDTO(
                            JudgeSourceTypeEnum.PROCESS_GATEWAY_NODE, copyNode.getEnterpriseId(), node.getNodeId(), copyNode.getNodeId(), controlIdMap));
                    copyNode.setGatewayConfig(JSON.toJSONString(gatewayConfigDTO));
                }
            }

            logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, copyNode);
            nodeIdMap.put(node.getNodeId(), copyNode.getNodeId());

            // 更新源映射信息
            if (updateSource && !node.getSourceNodeId().equals(copyNode.getNodeId())) {
                node.setSourceNodeId(copyNode.getNodeId());
                ApplicationProcessNode applicationProcessNode = BeanUtil.copyProperties(node, ApplicationProcessNode.class);
                logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, applicationProcessNode);
            }

            // 复制机器人节点
            this.copyRobotNode(nodeTypeEnum, node, copyNode, controlIdMap, updateSource);

            // 复制流程节点处理人
            this.copyNodeHandler(nodeTypeEnum, node, copyNode, copyNodeDTO);

            Long sourceId = node.getNodeId();
            Long targetId = copyNode.getNodeId();
            Long enterpriseId = copyNode.getEnterpriseId();
            // 复制流程节点待办转让人
            applicationPermissionService.copyPermission(new CopyAppPermissionReqDTO(
                    ApplicationSourceTypeEnum.PROCESS_NODE_PENDING_DIVERT, enterpriseId, sourceId, targetId));

            // 复制流程节点超时提醒人
            applicationPermissionService.copyPermission(new CopyAppPermissionReqDTO(
                    ApplicationSourceTypeEnum.PROCESS_NODE_OVERTIME_NOTIFIER, enterpriseId, sourceId, targetId));

            // 复制流程控件配置
            controlPermissionService.copyControlPermissions(new CopyControlPermissionReqDTO(
                    ControlPermissionSourceTypeEnum.PROCESS_NODE, enterpriseId, node.getNodeId(), targetId, controlIdMap));

            // 递归复制
            this.recursionCopyNode(targetSourceNodeMap, targetId, node.getChildren(), copyNodeDTO, updateSource);
        });
    }

    /**
     * 复制机器人节点
     *
     * @param nodeTypeEnum nodeTypeEnum
     * @param source       source
     * @param target       target
     * @param controlIdMap controlIdMap
     * @param updateSource updateSource
     */
    private void copyRobotNode(NodeTypeEnum nodeTypeEnum, ApplicationProcessNode source, ApplicationProcessNode target, Map<Long, Long> controlIdMap, boolean updateSource) {
        if (nodeTypeEnum.isRobot()) {
            // 机器人节点
            if (null != target.getGatewayConfig()) {
                // 复制条件判断
                applicationJudgeService.copyJudges(new CopyJudgeReqDTO(
                        JudgeSourceTypeEnum.PROCESS_ROBOT_NODE_SEARCH, source.getNodeId(), target.getNodeId(), null, controlIdMap));
                // 复制机器人规则
                applicationRobotRuleService.copyRobotRule(new CopyRobotRuleReqDTO(
                        RobotRuleSourceTypeEnum.PROCESS_NODE_ROBOT, target.getEnterpriseId(), source.getNodeId(), target.getNodeId(), controlIdMap, updateSource));
            }
        }
    }

    /**
     * 复制流程节点处理人
     *
     * @param nodeTypeEnum nodeTypeEnum
     * @param source       source
     * @param target       target
     * @param copyNodeDTO  copyNodeDTO
     */
    private void copyNodeHandler(NodeTypeEnum nodeTypeEnum, ApplicationProcessNode source, ApplicationProcessNode target, CopyNodeDTO copyNodeDTO) {
        if (!nodeTypeEnum.isHasHandler()) {
            return;
        }
        ApplicationSourceTypeEnum sourceTypeEnum = ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER;
        Long sourceId = source.getNodeId();
        Long targetId = target.getNodeId();
        Long enterpriseId = target.getEnterpriseId();
        try {
            CopyAppPermissionReqDTO copyAppPermissionReqDTO = new CopyAppPermissionReqDTO(sourceTypeEnum, enterpriseId, sourceId, targetId, copyNodeDTO.getControlIdMap());
            // 权限变动通知
            ApplicationInfo applicationInfo = applicationInfoMapper.findById(copyNodeDTO.getProcess().getApplicationId());
            copyAppPermissionReqDTO.setNotice(new NoticeBaseDTO(applicationInfo.getApplicationName(), source.getNodeName()));
            applicationPermissionService.copyPermission(copyAppPermissionReqDTO);
        } catch (ApplicationException applicationException) {
            // 节点应该有处理人 却没有处理人
            copyNodeDTO.getErrorMsgList().add(String.format("节点[%s]处理人不能为空", source.getNodeName()));
        }
    }

    /**
     * 删除流程节点
     *
     * @param deletedNodes deletedNodes
     */
    private void deleteNodes(List<ApplicationProcessNode> deletedNodes) {
        deletedNodes.forEach(processNode -> {
            processNode.setIsDeleted(SwitchEnum.NO.getId());

            logService.saveAndCheck(this.baseMapper, ApplicationProcessNode::getNodeId, processNode);

            // 删除应用权限
            int sourceType = ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER.getId();
            applicationPermissionService.deleteBySource(new AppPermissionSourceDTO(sourceType, processNode.getNodeId()));

            // 删除流程节点控件权限
            List<Long> nodeIds = deletedNodes.stream().map(ApplicationProcessNode::getNodeId).collect(Collectors.toList());
            controlPermissionService.deleteControlPermissions(new ControlPermissionsReqDTO(ControlPermissionSourceTypeEnum.PROCESS_NODE, nodeIds));
        });
    }

    /**
     * 检查机器人节点是否成环
     *
     * @param sourceAppId sourceAppId
     * @param targetAppId targetAppId
     */
    private void checkRobotNodeSurround(Long sourceAppId, Long targetAppId) {
        if (sourceAppId.equals(targetAppId)) {
            // 同一个应用，成环
            throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "Robot不可成环");
        }
        // 发布版
        int releaseVersion = ApplicationVersionEnum.RELEASE_VERSION.getId();
        ApplicationProcess targetProcess = applicationProcessMapper.findByAppIdAndVersion(targetAppId, releaseVersion);

        // 获取目标应用所有未删除的流程节点
        List<ApplicationProcessNode> targetNodes = this.baseMapper.findByProcessId(targetProcess.getProcessId(), true);
        // 检查节点流程机器人
        targetNodes.forEach(node -> {
            NodeTypeEnum nodeTypeEnum = NodeTypeEnum.isExistById(node.getNodeType());
            if (nodeTypeEnum.isRobot() && StringUtils.isNotBlank(node.getRobotConfig())) {
                // 机器人节点
                RobotConfigDTO robotConfigDTO = JSON.parseObject(node.getRobotConfig(), RobotConfigDTO.class);
                // 理论是会有目标应用id的，但是可能目标应用被删除了，导致这里为空
                if (Objects.nonNull(robotConfigDTO.getTargetAppId())) {
                    if (sourceAppId.equals(robotConfigDTO.getTargetAppId())) {
                        // 目标应用流程机器人节点配置 源应用
                        throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "Robot不可成环");
                    }
                }
            }
        });
    }

    /**
     * 流程节点转DTO
     *
     * @param applicationProcess applicationProcess
     * @param nodes              nodes
     * @return List
     */
    private List<NodeRespInfoDTO> toNodeRespInfoDTO(ApplicationProcess applicationProcess, List<ApplicationProcessNode> nodes) {
        // 获取相应版本 表单控件
        ApplicationVersionEnum versionEnum = ApplicationVersionEnum.findById(applicationProcess.getApplicationVersion());
        Map<Long, ControlBaseInfoDTO> controlBaseMap = applicationFormControlService.findBaseInfoMapByAppId(
                applicationProcess.getApplicationId(), versionEnum, true);
        Set<Long> controlIds = controlBaseMap.keySet();

        List<Long> nodeIds = StreamUtil.transListT(nodes, ApplicationProcessNode::getNodeId);
        // 节点处理人
        Map<Long, List<AppPermissionDTO>> nodeHandlerMap = applicationPermissionService
                .findDTOByAppPermissions(new AppPermissionsReqDTO(ApplicationSourceTypeEnum.PROCESS_NODE_HANDLER.getId(), nodeIds));

        // 流程节点待办转让人
        Map<Long, List<AppPermissionDTO>> nodePendingDivertMap = applicationPermissionService
                .findDTOByAppPermissions(new AppPermissionsReqDTO(ApplicationSourceTypeEnum.PROCESS_NODE_PENDING_DIVERT.getId(), nodeIds));

        // 流程节点超时提醒人
        Map<Long, List<AppPermissionDTO>> nodeOvertimeNotifierMap = applicationPermissionService
                .findDTOByAppPermissions(new AppPermissionsReqDTO(ApplicationSourceTypeEnum.PROCESS_NODE_OVERTIME_NOTIFIER.getId(), nodeIds));

        // 节点控件配置
        Map<Long, Set<Long>> nodeControlIdsMap = nodeIds.stream().collect(Collectors.toMap(e -> e, e -> controlIds));
        Map<Long, List<ControlPermissionDTO>> controlPermissionMap = controlPermissionService.nodesControlPermissionMap(nodeIds, nodeControlIdsMap);

        // 获取流程结束节点
        ApplicationProcessNode endNode = this.baseMapper.findEndNodeByProcessId(applicationProcess.getProcessId());
        NodeRespInfoDTO endNodeDTO = this.createNodeRespDTO(endNode, controlIds);

        return nodes.stream().map(processNode -> {
            NodeRespInfoDTO nodeDTO = this.createNodeRespDTO(processNode, controlIds);
            nodeDTO.setEndNode(endNodeDTO);

            // 节点处理人
            nodeDTO.setHandlers(nodeHandlerMap.getOrDefault(processNode.getNodeId(), new ArrayList<>()));

            // 流程节点待办转让人
            nodeDTO.setPendingDiverts(nodePendingDivertMap.getOrDefault(processNode.getNodeId(), new ArrayList<>()));

            // 流程节点超时提醒人
            nodeDTO.setOvertimeReminds(nodeOvertimeNotifierMap.getOrDefault(processNode.getNodeId(), new ArrayList<>()));

            // 节点控件配置
            List<NodeControlSettingDTO> nodeControlSettings = controlPermissionMap.getOrDefault(processNode.getNodeId(), new ArrayList<>()).stream()
                    .map(e -> {
                        String controlTitle = "";
                        if (controlBaseMap.containsKey(e.getControlId())) {
                            controlTitle = controlBaseMap.get(e.getControlId()).getControlTitle();
                        }
                        return new NodeControlSettingDTO(processNode.getNodeId(), e.getControlId(), controlTitle, e.getVisible());
                    })
                    .collect(Collectors.toList());
            nodeDTO.setNodeControlSettings(nodeControlSettings);

            return nodeDTO;
        }).collect(Collectors.toList());
    }

    /**
     * 递归获取所有子级
     *
     * @param nodes           nodes
     * @param childrenNodeIds childrenNodeIds
     * @return List
     */
    private List<ApplicationProcessNode> recursionGetChildrenNodes(List<ApplicationProcessNode> nodes, List<Long> childrenNodeIds) {
        List<ApplicationProcessNode> childrenNodes = new ArrayList<>();

        if (!nodes.isEmpty()) {
            List<Long> nodeIds = nodes.stream().map(ApplicationProcessNode::getNodeId).collect(Collectors.toList());
            if (CollectionUtil.containsAny(childrenNodeIds, nodeIds)) {
                throw new BusinessException(RCodeEnum.ACTIVITY_PROCESS_NODE_ERROR);
            }
            childrenNodeIds.addAll(nodeIds);
            List<ApplicationProcessNode> childrenNodeList = this.baseMapper.findByParentNodeIds(nodeIds, true);
            childrenNodes.addAll(childrenNodeList);
            this.recursionGetChildrenNodes(childrenNodeList, childrenNodeIds);
        }
        return childrenNodes;
    }

    /**
     * 获取当前节点 之前所有节点
     *
     * @param nodeId nodeId
     * @return List
     */
    private List<NodeBaseDTO> getCanSettingFallbacks(Long nodeId) {
        ApplicationProcessNode node = this.baseMapper.findById(nodeId, true);
        return this.recursionGetParentNodes(node, new ArrayList<>()).stream()
                .filter(e -> NodeTypeEnum.isExistById(e.getNodeType()).canFallback())
                .map(e -> new NodeBaseDTO(e.getNodeId(), e.getNodeType(), e.getNodeName()))
                .collect(Collectors.toList());
    }

    /**
     * 递归获取节点 所有上级
     *
     * @param node          node
     * @param parentNodeIds 记录所有父级节点，用于记录是否出现层级错乱
     * @return List
     */
    private List<ApplicationProcessNode> recursionGetParentNodes(ApplicationProcessNode node, List<Long> parentNodeIds) {
        List<ApplicationProcessNode> parentNodes = new ArrayList<>();
        if (parentNodeIds.contains(node.getParentNodeId())) {
            throw new BusinessException(RCodeEnum.ACTIVITY_PROCESS_NODE_ERROR);
        }
        if (!ApplicationUtil.DEFAULT_PARENT_NODE_ID.equals(node.getParentNodeId())) {
            ApplicationProcessNode parentNode = this.baseMapper.findById(node.getParentNodeId(), true);
            parentNodes.add(parentNode);
            parentNodeIds.add(node.getParentNodeId());
            parentNodes.addAll(this.recursionGetParentNodes(parentNode, parentNodeIds));
        }
        return parentNodes;
    }

    /**
     * 递归获取指定节点 最近的上级网关
     *
     * @param node          node
     * @param parentNodeIds 记录所有父级节点，用于记录是否出现层级错乱
     * @return ApplicationProcessNode
     */
    private ApplicationProcessNode getFirstGatewayParentNode(ApplicationProcessNode node, List<Long> parentNodeIds) {
        if (parentNodeIds.contains(node.getParentNodeId())) {
            throw new BusinessException(RCodeEnum.ACTIVITY_PROCESS_NODE_ERROR);
        }
        if (!ApplicationUtil.DEFAULT_PARENT_NODE_ID.equals(node.getParentNodeId())) {
            ApplicationProcessNode parentNode = this.baseMapper.findById(node.getParentNodeId(), true);
            parentNodeIds.add(node.getParentNodeId());

            if (NodeTypeEnum.INCLUSIVE_GATEWAY_NODE.getId() == node.getNodeType()) {
                // 当前节点时网关
                return parentNode;
            }
            return this.getFirstGatewayParentNode(parentNode, parentNodeIds);
        }
        return null;
    }

    /**
     * 创建流程节点返回实体
     *
     * @param node node
     * @return NodeRespDTO
     */
    private NodeRespInfoDTO createNodeRespDTO(ApplicationProcessNode node, Collection<Long> controlIds) {
        NodeRespInfoDTO nodeRespDTO = new NodeRespInfoDTO();
        BeanUtil.copyProperties(node, nodeRespDTO, NodeInfoDTO.IGNORE_PROPERTIES);
        // 是否开启重新计算公式或关联数据
        if (nodeRespDTO.getRecalculation() == SwitchEnum.YES.getId()) {
            nodeRespDTO.setRecalculationControlIds(JSON.parseArray(node.getRecalculationControlIds(), Long.class));
        }

        NodeTypeEnum nodeTypeEnum = NodeTypeEnum.isExistById(node.getNodeType());
        // 网关配置
        if (StringUtils.isNotBlank(node.getGatewayConfig())) {
            // 网关配置(node_type为0时值有效)
            GatewayConfigDTO gatewayConfigDTO = JSON.parseObject(node.getGatewayConfig(), GatewayConfigDTO.class);

            // 过滤有效筛选条件
            gatewayConfigDTO.setJudgeOr(OrderDataUtil.filterJudgeOrs(gatewayConfigDTO.getJudgeOr(), controlIds, null));
            nodeRespDTO.setGatewayConfig(gatewayConfigDTO);
        }

        // 机器人节点
        if (nodeTypeEnum.isRobot()) {
            RobotConfigDTO robotConfigDTO;
            if (StringUtils.isNotBlank(node.getRobotConfig())) {
                robotConfigDTO = JSON.parseObject(node.getRobotConfig(), RobotConfigDTO.class);
            } else {
                robotConfigDTO = new RobotConfigDTO();
            }
            if (Objects.nonNull(robotConfigDTO.getTargetAppId())) {
                // 筛选条件
                List<JudgeOr> judgeOrs = applicationJudgeService.findDTOBySource(new JudgeSourceDTO(
                        JudgeSourceTypeEnum.PROCESS_ROBOT_NODE_SEARCH, node.getNodeId()));
                robotConfigDTO.setJudgeOrs(judgeOrs);

                // 机器人规则
                robotConfigDTO.setRobotRules(applicationRobotRuleService.findBySource(
                        new RobotRuleSourceDTO(RobotRuleSourceTypeEnum.PROCESS_NODE_ROBOT, node.getNodeId())));
            }
            nodeRespDTO.setRobotConfig(robotConfigDTO);
        }

        // 节点提交重新更新字段关联数据配置
        if (StringUtils.isNotBlank(node.getSubmitRefreshConfig())) {
            List<SubmitRefreshConfigDTO> submitRefreshConfigDTOS = JSON.parseArray(
                    node.getSubmitRefreshConfig(), SubmitRefreshConfigDTO.class);
            nodeRespDTO.setSubmitRefreshConfig(submitRefreshConfigDTOS);
        }

        // 流程回退配置
        if (StringUtils.isNotBlank(node.getNodeFallbackConfig())) {
            FallbackConfigDTO fallbackConfigDTO = JSON.parseObject(node.getNodeFallbackConfig(), FallbackConfigDTO.class);
            nodeRespDTO.setNodeFallbackConfig(fallbackConfigDTO);
        }

        // 流程按钮配置
        if (StringUtils.isNotBlank(node.getNodeButtonConfig())) {
            NodeButtonConfigDTO nodeButtonConfigDTO = JSON.parseObject(node.getNodeButtonConfig(), NodeButtonConfigDTO.class);
            nodeRespDTO.setNodeButtonConfig(nodeButtonConfigDTO);
        }

        return nodeRespDTO;
    }

    /**
     * 创建应用流程节点
     *
     * @param process process
     * @return ApplicationProcessNode
     */
    private ApplicationProcessNode createProcessNode(ApplicationProcess process) {
        ApplicationProcessNode applicationProcessNode = new ApplicationProcessNode();

        applicationProcessNode.setEnterpriseId(process.getEnterpriseId());
        applicationProcessNode.setProcessId(process.getProcessId());
        applicationProcessNode.setGatewayConfig(JSON.toJSONString(new GatewayConfigDTO()));
        applicationProcessNode.setSubmitRefreshConfig(JSON.toJSONString(new ArrayList<>()));
        applicationProcessNode.setNodeFallbackConfig(JSON.toJSONString(new FallbackConfigDTO()));
        applicationProcessNode.setIsDeleted(SwitchEnum.NO.getId());

        return applicationProcessNode;
    }
}
